package com.zshy.base.core.utils;

import cn.hutool.core.text.CharSequenceUtil;
import com.zshy.base.core.contract.enums.IEnum;
import com.zshy.base.core.contract.enums.ResultCodeEnum;
import com.zshy.base.core.contract.exception.ServiceErrorException;
import com.zshy.base.core.contract.exception.ServiceException;
import lombok.experimental.UtilityClass;

import java.lang.reflect.Array;
import java.math.BigDecimal;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;

/**
 * 业务断言类，断言不通过将抛出ServiceException
 *
 * @author zhouhengzhe
 * @公众号 小白的Java进阶之路
 */
@UtilityClass
public class BizAssert {

    private static final String DEFAULT_ERROR_MSG = "Assertion failed";

    /**
     * 抛出异常方法
     *
     * @param condition 是否抛出异常的条件
     * @param code 异常码
     * @param errorMsg 异常信息模板
     * @param params 异常信息模板中占位符对应的参数列表
     * @throws ServiceException 当满足抛出异常的条件时，抛出指定的异常码和格式化后的异常信息
     */
    private void throwException(boolean condition, int code, String errorMsg, Object... params) {
        // 如果满足抛出异常的条件
        if (condition) {
            // 抛出指定的异常码和格式化后的异常信息
            throw new ServiceException(code, CharSequenceUtil.format(errorMsg, params));
        }
    }

    /**
     * 抛出异常
     *
     * @param condition 是否抛出异常的条件
     * @param iEnum     异常枚举类，用于获取异常码
     * @throws ServiceException 如果满足抛出异常的条件，则抛出异常码对应的ServiceException异常
     */
    private void throwException(boolean condition, IEnum<Integer> iEnum) {
        // 如果满足抛出异常的条件
        if (condition) {
            // 抛出指定的异常码和格式化后的异常信息
            throw new ServiceException(iEnum);
        }
    }
    /**
     * 抛出异常方法
     *
     * @param condition 是否抛出异常的条件
     * @param errorMsg 异常信息
     * @throws ServiceException 抛出指定的异常码和格式化后的异常信息
     */
    private void throwException(boolean condition, String errorMsg) {
        // 如果满足抛出异常的条件
        if (condition) {
            // 抛出指定的异常码和格式化后的异常信息
            throw new ServiceException(errorMsg);
        }
    }

    /**
     * 抛出异常方法
     *
     * @param condition 是否抛出异常的条件
     * @throws ServiceException 如果满足抛出异常的条件，则抛出ServiceException异常
     */
    private void throwException(boolean condition) {
        // 如果满足抛出异常的条件
        if (condition) {
            // 抛出指定的异常码和格式化后的异常信息
            throw new ServiceException(ResultCodeEnum.FAIL.getCode(), DEFAULT_ERROR_MSG);
        }
    }

    /**
     * 断言表达式为false时，报错
     *
     * @param expression 表达式
     * @param code       错误码
     * @param errorMsg   错误信息
     * @param params     错误信息的参数,一般指的是errorMsg里面的%s的填充值
     * @throws ServiceException 业务异常错误
     */
    public void assertFalseWarn(Boolean expression, Integer code, String errorMsg, Object... params) {
        throwException(!expression, code, errorMsg, params);

    }

    /**
     * 断言表达式为false，报错
     *
     * @param expression 表达式
     * @param errorMsg   错误信息
     * @param params     错误信息的参数,一般指的是errorMsg里面的%s的填充值
     * @throws ServiceException 业务异常错误
     */
    public void assertFalseWarn(Boolean expression, String errorMsg, Object... params) {
        throwException(!expression, ResultCodeEnum.FAIL.getCode(), errorMsg, params);
    }

    /**
     * 断言表达式为false，报错
     *
     * @param expression 表达式
     * @param iEnum      错误枚举
     * @throws ServiceException 业务异常错误
     */
    public void assertFalseWarn(Boolean expression, IEnum<Integer> iEnum) {
        throwException(!expression, iEnum);
    }

    /**
     * 断言表达式为false，报错
     *
     * @param expression 表达式
     * @param iEnum      错误枚举
     * @throws ServiceErrorException Error业务异常错误
     */
    public void assertFalseError(Boolean expression, IEnum<Integer> iEnum) {
        if (!expression) {
            throw new ServiceErrorException(iEnum);
        }
    }

    /**
     * 断言表达式为true，报错
     *
     * @param expression 表达式
     * @param errorMsg   错误信息
     * @param params     错误信息的参数,一般指的是errorMsg里面的%s的填充值
     * @throws ServiceException 业务异常错误
     */
    public void assertTrueWarn(Boolean expression, Integer code, String errorMsg, Object... params) {
        throwException(expression, code, errorMsg, params);
    }

    /**
     * 等价于 a.compareTo(b) < 0
     *
     * @param a      比较值a
     * @param b      比较值b
     * @param code   错误码
     * @param ifLess 错误信息
     * @throws ServiceException 业务异常错误
     */
    public void assertGeWarn(BigDecimal a, BigDecimal b, Integer code, String ifLess) {
        throwException(a == null || b == null || a.compareTo(b) < 0, code, ifLess);
    }

    /**
     * 等价于 a.compareTo(b) < 0
     *
     * @param a 比较值a
     * @param b 比较值b
     * @throws ServiceException 业务异常错误
     */
    public void assertGeWarn(BigDecimal a, BigDecimal b, IEnum<Integer> iEnum) {
        throwException(a == null || b == null || a.compareTo(b) < 0, iEnum);
    }

    /**
     * 等价于 a.compareTo(b) < 0
     *
     * @param a 比较值a
     * @param b 比较值b
     * @throws ServiceErrorException 业务异常错误
     */
    public void assertGeError(BigDecimal a, BigDecimal b, IEnum<Integer> iEnum) {
        if (a == null || b == null || a.compareTo(b) < 0) {
            throw new ServiceErrorException(iEnum);
        }
    }

    /**
     * 等价于 a.compareTo(b) < 0
     *
     * @param a      比较值a
     * @param b      比较值b
     * @param ifLess 错误信息
     * @throws ServiceException 业务异常错误
     */
    public void assertGeWarn(BigDecimal a, BigDecimal b, String ifLess) {
        throwException(a == null || b == null || a.compareTo(b) < 0, ifLess);
    }

    /**
     * 等价于 a.compareTo(b) < 0
     *
     * @param a 比较值a
     * @param b 比较值b
     * @throws ServiceException 业务异常错误
     */
    public void assertGeWarn(BigDecimal a, BigDecimal b) {
        throwException(a == null || b == null || a.compareTo(b) < 0);
    }

    /**
     * 等价于 a.compareTo(b) <= 0
     *
     * @param a      比较值a
     * @param b      比较值b
     * @param code   错误码
     * @param ifLess 错误信息
     * @throws ServiceException 业务异常错误
     */
    public void assertGtWarn(BigDecimal a, BigDecimal b, Integer code, String ifLess) {
        throwException(a == null || b == null || a.compareTo(b) <= 0, code, ifLess);
    }

    /**
     * 等价于 a.compareTo(b) <= 0
     *
     * @param a      比较值a
     * @param b      比较值b
     * @param ifLess 错误信息
     * @throws ServiceException 业务异常错误
     */
    public void assertGtWarn(BigDecimal a, BigDecimal b, String ifLess) {
        throwException(a == null || b == null || a.compareTo(b) <= 0, ifLess);
    }

    /**
     * 等价于 a.compareTo(b) <= 0
     *
     * @param a 比较值a
     * @param b 比较值b
     * @throws ServiceException 业务异常错误
     */
    public void assertGtWarn(BigDecimal a, BigDecimal b) {
        throwException(a == null || b == null || a.compareTo(b) <= 0);
    }

    /**
     * 断言表达式为true时，报错
     * <p>
     * 等价于expression==true
     *
     * @param expression 表达式
     * @param iEnum      错误枚举
     * @throws ServiceException 业务异常错误
     */
    public void assertTrueWarn(Boolean expression, IEnum<Integer> iEnum) {
        throwException(expression, iEnum);
    }

    /**
     * 断言表达式为true时，报错
     * <p>
     * 等价于expression==true
     *
     * @param expression 表达式
     * @param iEnum      错误枚举
     * @throws ServiceErrorException 业务异常错误
     */
    public void assertTrueError(Boolean expression, IEnum<Integer> iEnum) {
        if (expression) {
            throw new ServiceErrorException(iEnum);
        }
    }

    /**
     * 断言表达式为空时，报错
     * <p>
     * 等价于 object==null
     *
     * @param object   对象
     * @param errorMsg 错误信息
     * @param params   错误信息的参数,一般指的是errorMsg里面的%s的填充值
     * @throws ServiceException 业务异常错误
     */
    public <T> void assertNullWarn(T object, String errorMsg, Object... params) {
        throwException(object == null, ResultCodeEnum.FAIL.getCode(), errorMsg, params);
    }

    /**
     * 断言表达式为空时，报错
     * <p>
     * 等价于 object==null
     *
     * @param object 对象
     * @param iEnum  错误枚举类
     * @throws ServiceException 业务异常错误
     */
    public <T> void assertNullWarn(T object, IEnum<Integer> iEnum) {
        throwException(object == null, iEnum);
    }

    /**
     * 断言表达式为空时，报错
     * <p>
     * 等价于 object==null
     *
     * @param object 对象
     * @param iEnum  错误枚举类
     * @throws ServiceErrorException 业务异常错误
     */
    public <T> void assertNullError(T object, IEnum<Integer> iEnum) {
        if (object == null) {
            throw new ServiceErrorException(iEnum);
        }
    }

    /**
     * 断言表达式不为空时，报错
     * <p>
     * 等价于 object!=null
     *
     * @param object 对象
     * @param iEnum  错误枚举
     * @param <T>    对象类型
     * @throws ServiceException 业务异常错误
     */
    public <T> void assertNotNullWarn(T object, IEnum<Integer> iEnum) {
        throwException(object != null, iEnum);
    }

    /**
     * 断言表达式为空时，报错
     * <p>
     * 等价于 object==null
     *
     * @param object 对象
     * @param <T>    对象类型
     * @throws ServiceException 业务异常错误
     */
    public <T> void assertNullWarn(T object) {
        throwException(object == null, ResultCodeEnum.ARGUMENT_NULL);
    }

    /**
     * 当字符串为空时，报错
     * <p>
     * 等价于content.isEmpty()
     *
     * @param content  字符串信息
     * @param code     错误码
     * @param errorMsg 错误信息
     * @param params   错误信息的参数,一般指的是errorMsg里面的%s的填充值
     * @throws ServiceException 业务异常错误
     */
    public void assertBlankWarn(String content, Integer code, String errorMsg, Object... params) {
        throwException(content == null || content.isEmpty(), code, errorMsg, params);
    }

    /**
     * 当字符串为空时，报错
     * <p>
     * 等价于content.isEmpty()
     *
     * @param content 字符串信息
     * @param iEnum   错误枚举
     * @throws ServiceException 业务异常错误
     */
    public void assertBlankWarn(String content, IEnum<Integer> iEnum) {
        throwException(content == null || content.isEmpty(), iEnum);
    }

    /**
     * 当字符串为空时，报错
     * <p>
     * 等价于content.isEmpty()
     *
     * @param content 字符串信息
     * @param iEnum   错误枚举
     * @throws ServiceException 业务异常错误
     */
    public void assertBlankError(String content, IEnum<Integer> iEnum) {
        if (content == null || content.isEmpty()) {
            throw new ServiceErrorException(iEnum);
        }
    }

    /**
     * 当字符串为空时，报错
     * <p>
     * 等价于content.isEmpty()
     *
     * @param content 字符串信息
     * @throws ServiceException 业务异常错误
     */
    public void assertBlankWarn(String content) {
        throwException(content == null || content.isEmpty(), ResultCodeEnum.ARGUMENT_NULL);
    }

    /**
     * 当集合数据为空时，报错
     * <p>
     * 等价于CollectionUtils.isEmpty(iterable)
     *
     * @param iterable 字符串信息
     * @param iEnum    错误枚举
     * @throws ServiceException 业务异常错误
     */
    public <E, T extends Iterable<E>> T assertEmptyWarn(T iterable, IEnum iEnum) {
        if (iterable == null || !iterable.iterator().hasNext()) {
            throw new ServiceException(iEnum);
        }
        return iterable;
    }

    /**
     * 判断集合为空，报错
     * <p>
     * 等价于CollectionUtils.isEmpty(iterable)
     *
     * @param iterable 集合
     * @param iEnum    错误枚举
     * @param <E>      集合元素类型
     * @param <T>      集合类型
     * @return 集合
     * @throws ServiceErrorException 业务异常错误
     */
    public <E, T extends Iterable<E>> T assertEmptyError(T iterable, IEnum<Integer> iEnum) {
        if (iterable == null || !iterable.iterator().hasNext()) {
            throw new ServiceErrorException(iEnum);
        }
        return iterable;
    }

    /**
     * 判断集合为空，报错
     * <p>
     * 等价于CollectionUtils.isEmpty(iterable)
     *
     * @param iterable 集合
     * @param <E>      集合元素类型
     * @param <T>      集合类型
     * @return 集合
     * @throws ServiceException 业务异常错误
     */
    public <E, T extends Iterable<E>> T assertEmptyWarn(T iterable) {
        if (iterable == null || !iterable.iterator().hasNext()) {
            throw new ServiceException(ResultCodeEnum.ARGUMENT_NULL);
        }
        return iterable;
    }

    /**
     * a与b不相等时，报错
     * <p>
     * a!=b
     *
     * @param a              对比对象a
     * @param b              对比对象b
     * @param code           错误码
     * @param notEqualsThrow 不相等的错误信息
     * @param params         不相等的错误信息的参数,一般指的是notEqualsThrow里面的%s的填充值
     * @throws ServiceException 业务异常错误
     */
    public void assertNotEqualsWarn(Object a, Object b, Integer code, String notEqualsThrow, Object... params) {
        throwException(!Objects.equals(a, b), code, notEqualsThrow, params);
    }

    /**
     * a与b不相等时，报错
     * <p>
     * a!=b
     *
     * @param a              对比对象a
     * @param b              对比对象b
     * @param notEqualsThrow 不相等的错误信息
     * @param params         不相等的错误信息的参数,一般指的是notEqualsThrow里面的%s的填充值
     * @throws ServiceException 业务异常错误
     */
    public void assertNotEqualsWarn(Object a, Object b, String notEqualsThrow, Object... params) {
        throwException(!Objects.equals(a, b), ResultCodeEnum.FAIL.getCode(), notEqualsThrow, params);

    }

    /**
     * a与b不相等时，报错
     * <p>
     * a!=b
     *
     * @param a 对比对象a
     * @param b 对比对象b
     * @throws ServiceException 业务异常错误
     */
    public void assertNotEqualsWarn(Object a, Object b, IEnum iEnum) {
        throwException(!Objects.equals(a, b), iEnum);
    }

    /**
     * a与b相等时，报错
     * <p>
     * a=b
     *
     * @param a           对比对象a
     * @param b           对比对象b
     * @param code        错误码
     * @param equalsThrow 相等的错误信息
     * @param params      相等的错误信息的参数,一般指的是notEqualsThrow里面的%s的填充值
     * @throws ServiceException 业务异常错误
     */
    public void assertEqualsWarn(Object a, Object b, Integer code, String equalsThrow, Object... params) {
        throwException(Objects.equals(a, b), code, equalsThrow, params);
    }

    /**
     * a与b相等时，报错
     * <p>
     * a=b
     *
     * @param a           对比对象a
     * @param b           对比对象b
     * @param equalsThrow 相等的错误信息
     * @param params      相等的错误信息的参数,一般指的是notEqualsThrow里面的%s的填充值
     * @throws ServiceException 业务异常错误
     */
    public void assertEqualsWarn(Object a, Object b, String equalsThrow, Object... params) {
        throwException(Objects.equals(a, b), ResultCodeEnum.FAIL.getCode(), equalsThrow, params);
    }


    /**
     * a与b相等时，报错
     * <p>
     * a=b
     *
     * @param a     对比对象a
     * @param b     对比对象b
     * @param iEnum 错误枚举
     * @throws ServiceException 业务异常错误
     */
    public void assertEqualsWarn(Object a, Object b, IEnum iEnum) {
        throwException(Objects.equals(a, b), iEnum);
    }

    /**
     * a与b相等时，报错
     * <p>
     * a=b
     *
     * @param a              对比对象a
     * @param b              对比对象b
     * @param notEqualsThrow 不相等的错误信息
     * @param params         不相等的错误信息的参数,一般指的是notEqualsThrow里面的%s的填充值
     * @throws ServiceErrorException 业务异常错误
     */
    public void assertNotEqualsError(Object a, Object b, Integer code, String notEqualsThrow, Object... params) {
        throwException(!Objects.equals(a, b), code, notEqualsThrow, params);
    }

    /**
     * a与b不相等时，报错
     * <p>
     * a！=b
     *
     * @param a              对比对象a
     * @param b              对比对象b
     * @param notEqualsThrow 不相等的错误信息
     * @param params         不相等的错误信息的参数,一般指的是notEqualsThrow里面的%s的填充值
     * @throws ServiceErrorException 业务异常错误
     */
    public void assertNotEqualsError(Object a, Object b, String notEqualsThrow, Object... params) {
        throwException(!Objects.equals(a, b), ResultCodeEnum.FAIL.getCode(), notEqualsThrow, params);
    }

    /**
     * a与b不相等时，报错
     * <p>
     * a！=b
     *
     * @param a     对比对象a
     * @param b     对比对象b
     * @param iEnum 错误枚举
     * @throws ServiceErrorException 业务异常错误
     */
    public void assertNotEqualsError(Object a, Object b, IEnum iEnum) {
        throwException(!Objects.equals(a, b), iEnum);
    }

    /**
     * a与b相等时，报错
     * <p>
     * a=b
     *
     * @param a           对比对象a
     * @param b           对比对象b
     * @param equalsThrow 相等的错误信息
     * @param params      相等的错误信息的参数,一般指的是notEqualsThrow里面的%s的填充值
     * @throws ServiceErrorException 业务异常错误
     */
    public void assertEqualsError(Object a, Object b, Integer code, String equalsThrow, Object... params) {
        throwException(Objects.equals(a, b), code, equalsThrow, params);
    }

    /**
     * a与b相等时，报错
     * <p>
     * a=b
     *
     * @param a           对比对象a
     * @param b           对比对象b
     * @param equalsThrow 相等的错误信息
     * @param params      相等的错误信息的参数,一般指的是notEqualsThrow里面的%s的填充值
     * @throws ServiceErrorException 业务异常错误
     */
    public void assertEqualsError(Object a, Object b, String equalsThrow, Object... params) {
        throwException(Objects.equals(a, b), ResultCodeEnum.FAIL.getCode(), equalsThrow, params);
    }

    /**
     * a与b相等时，报错
     * <p>
     * a=b
     *
     * @param a     对比对象a
     * @param b     对比对象b
     * @param iEnum 错误枚举
     * @throws ServiceErrorException 业务异常错误
     */
    public void assertEqualsError(Object a, Object b, IEnum iEnum) {
        throwException(Objects.equals(a, b), iEnum);
    }


    public void assertHasValue(Object object, Integer code, String missingValue) {
        if (object == null) {
            throw new ServiceException(code, missingValue);
        }
        if (object instanceof Iterable) {
            // 非空集合
            if (!((Iterable<?>) object).iterator().hasNext()) {
                throw new ServiceException(code, missingValue);
            }
        } else if (object.getClass().isArray()) {
            // 非空数组
            if (Array.getLength(object) == 0) {
                throw new ServiceException(code, missingValue);
            }
        } else if (object instanceof String) {
            // 非空字符串
            if (((String) object).isEmpty()) {
                throw new ServiceException(code, missingValue);
            }
        } else if (object instanceof Map) {
            // 非空Map
            if (((Map<?, ?>) object).isEmpty()) {
                throw new ServiceException(code, missingValue);
            }
        } else if (object instanceof Optional) {
            // 非空Optional
            if (!((Optional<?>) object).isPresent()) {
                throw new ServiceException(code, missingValue);
            }
        } else {
            // 对于其他对象类型，如果需要进一步检查，这里可以添加逻辑，
            // 或者直接认为非null的对象就是有效的。
        }
    }

    public void assertHasValue(Object object, String ifEmpty) {
        assertHasValue(object, ResultCodeEnum.FAIL.getCode(), ifEmpty);
    }

    public void assertHasValue(Object dontEmpty) {
        assertHasValue(dontEmpty, ResultCodeEnum.FAIL.getDesc());
    }
}
